/**
 * 该文件（`packages/core-common/src/event-bus/event-decorator.ts`）实现了基于装饰器的事件自动绑定机制，核心逻辑如下：
 *
 * 1. **WithEventBus 基类**
 *     - 继承自 `Disposable`，通过构造函数参数接收 `eventBus` 实例。
 *     - 构造函数中读取类上通过装饰器注册的事件映射（`EVENT_TOKEN`），为每个事件类型自动注册监听器，并在触发时调用对应方法。
 *     - 所有监听器的销毁函数会被收集，便于统一释放。
 *
 * 2. **OnEvent 装饰器**
 *     - 用于修饰类方法，将方法与事件类型（`Constructor`）关联。
 *     - 通过简化的元数据机制，将事件类型和方法名的映射存储到类上（`EVENT_TOKEN`）。
 *
 * **总结**：
 * 本文件为事件监听提供了声明式装饰器写法，开发者只需用 `@OnEvent` 标注方法并继承 `WithEventBus`，即可自动完成事件注册和解绑，简化事件驱动开发。
 */
import { BasicEvent } from './basic-event';
import { IEventBus } from './event-bus-types';
import { Disposable } from '../../utils';
import { ConstructorOf } from '../../utils/declare';

const EVENT_TOKEN = Symbol('EVENT_TOKEN');

// 简化的元数据存储
const metadataMap = new WeakMap<any, Map<symbol, any>>();

// 简化的 Reflect 元数据实现
const defineMetadata = (key: symbol, value: any, target: any) => {
  if (!metadataMap.has(target)) {
    metadataMap.set(target, new Map());
  }
  metadataMap.get(target)!.set(key, value);
};

const getMetadata = (key: symbol, target: any) => {
  const targetMap = metadataMap.get(target);
  return targetMap ? targetMap.get(key) : undefined;
};

export class WithEventBus extends Disposable {
  protected eventBus: IEventBus;

  constructor(eventBus: IEventBus, ...args: any[]) {
    super(...args);
    this.eventBus = eventBus;

    const map: Map<string, ConstructorOf<any>> = getMetadata(
      EVENT_TOKEN,
      this
    ) || new Map();
    for (const [key, Constructor] of map.entries()) {
      const dispose = this.eventBus.on(Constructor, (event: any) =>
        (this as any)[key](event)
      );
      this.addDispose(dispose);
    }
  }
}

export function OnEvent<T extends BasicEvent<any>>(
  Constructor: ConstructorOf<T>
) {
  return (
    target: object,
    key: string,
    descriptor: TypedPropertyDescriptor<(event: T) => void>
  ) => {
    const map: Map<string, ConstructorOf<any>> = getMetadata(
      EVENT_TOKEN,
      target
    ) || new Map();
    map.set(key, Constructor);
    defineMetadata(EVENT_TOKEN, map, target);
  };
}
